#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@File    : maze.py
@Time    : 2022/11/02 21:53:59
@Author  : 郭瑞强
@Contact : sunraing@126.com
@Version : 0.1
@License : BSD 3-Clause License
@Desc    : 探索迷宫问题
"""

import turtle
from enum import Enum


class State(Enum):
    PART_OF_PATH = "P"
    OBSTACLE = "+"
    TRIED = "T"
    DEAD_END = "D"


class Maze:
    """迷宫"""

    def __init__(self, maze_filename):
        """迷宫矩阵初始化
        self.maze_list
        self.rows
        self.cols
        self.start_row
        self.start_col
        self.t
        Args:
            maze_filename (file): 存储迷宫信息的文本文件
        """
        self.maze_list = []  # 迷宫矩阵
        # 需要注意, 通常最后一行为首行, 与读取顺序正好相反, 需要上下反转一次

        with open(maze_filename, "r") as maze_file:
            maze_text_ls = maze_file.readlines()

        maze_text_ls.reverse()  # 迷宫矩阵上下反转
        # 定位小乌龟
        for row, line in enumerate(maze_text_ls):
            idx = line.find("S")
            if idx != -1:
                self.start_row = row
                self.start_col = idx
                print("start position:({},{})".format(self.start_row, self.start_col))
            self.maze_list.append(list(line))

        self.rows = len(self.maze_list)  # 迷宫行数
        self.cols = len(self.maze_list[0])  # 迷宫列数
        print("maze size:{}x{}".format(self.rows, self.cols))

        self.t = turtle.Turtle(shape="turtle")

        turtle.setworldcoordinates(-0.5, -0.5, self.cols, self.rows)

    def draw_maze(self):
        turtle.tracer(0)
        for row in range(self.rows):
            for col in range(self.cols):
                if self.maze_list[row][col] == State.OBSTACLE.value:
                    self.draw_centered_box(col, row, "tan")

        turtle.tracer(1)
        self.t.color("black", "blue")

    # 以坐标为中心画方块key_bindings
    def draw_centered_box(self, x, y, color):
        self.t.up()
        self.t.goto(x - 0.5, y - 0.5)
        self.t.color("black", color)
        self.t.setheading(90)
        self.t.down()
        self.t.begin_fill()
        for i in range(4):
            self.t.forward(1)
            self.t.right(90)
        self.t.end_fill()
        turtle.update()

    def move_turtle(self, row, col):
        self.t.up()
        self.t.setheading(self.t.towards(col, row))
        if self.maze_list[row][col] != State.OBSTACLE.value:
            self.t.goto(col, row)

    def drop_breadcrumb(self, color):
        self.t.dot(color)

    def update_pos(self, row, col, val=None):
        if val:
            self.maze_list[row][col] = val
        self.move_turtle(row, col)

        match val:
            case State.PART_OF_PATH.value:
                color = "green"
            case State.OBSTACLE.value:
                color = "red"
            case State.TRIED.value:
                color = "black"
            case State.DEAD_END.value:
                color = "red"
            case _:
                color = None

        if color:
            self.drop_breadcrumb(color)

    def is_exits(self, row, col) -> bool:
        return row == 0 or row == self.rows - 1 or col == 0 or col == self.cols - 1

    def __getitem__(self, idx):
        return self.maze_list[idx]

    def pouse(self):
        turtle.done()


def search_form(maze: Maze, start_row, start_col) -> bool:
    maze.update_pos(start_row, start_col)

    # 检查基本情况
    # 1. 遇到墙
    if maze[start_row][start_col] == State.OBSTACLE.value:
        return False

    # 2. 已经走过的路
    if maze[start_row][start_col] == State.TRIED.value:
        return False

    # 3. 找到出口
    if maze.is_exits(start_row, start_col):
        maze.update_pos(start_row, start_col, State.PART_OF_PATH.value)
        return True

    maze.update_pos(start_row, start_col, State.TRIED.value)

    # 否则依次尝试4个方向
    found = (
        search_form(maze, start_row - 1, start_col)
        or search_form(maze, start_row + 1, start_col)
        or search_form(maze, start_row, start_col - 1)
        or search_form(maze, start_row, start_col + 1)
    )

    if found:
        maze.update_pos(start_row, start_col, State.PART_OF_PATH.value)
    else:
        maze.update_pos(start_row, start_col, State.DEAD_END.value)
    return found
